package com.hongyi.common.mongoDB.service.impl;

import com.hongyi.common.mongoDB.service.MongoDBService;
import com.hongyi.common.mongoDB.utils.MongoPage;
import com.mongodb.client.MongoClient;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

/**
 * MongoDB 处理类
 */
@Repository
public class MongoDBServiceImpl implements MongoDBService {

    @Resource
    private MongoTemplate mongoTemplate;

    @Resource
    private MongoPage mongoPage;

    @Resource
    private MongoClient mongoClient;

    @Override
    public boolean createCollection(String collectionName) {
        if (mongoTemplate.collectionExists(collectionName)) {
            return true;
        }
        mongoTemplate.createCollection(collectionName);
        return mongoTemplate.collectionExists(collectionName);
    }

    @Override
    public boolean dropCollection(String collectionName) {
        if (!mongoTemplate.collectionExists(collectionName)) {
            return true;
        }
        mongoTemplate.getCollection(collectionName).drop();
        return !mongoTemplate.collectionExists(collectionName);
    }

    @Override
    public boolean insert(Object o, String collectionName) {
        try {
            mongoTemplate.insert(o, collectionName);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean insert(Object o) {
        try {
            mongoTemplate.insert(o);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean insertAll(List o, String collectionName) {
        try {
            mongoTemplate.insert(o, collectionName);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean insertAll(List o, Class c) {
        try {
            mongoTemplate.insert(o, c);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public void save(Object o, String collectionName) {
        mongoTemplate.save(o, collectionName);
    }

    @Override
    public Object findAll(Class c, String collectionName) {
        return mongoTemplate.findAll(c, collectionName);
    }

    @Override
    public Object findById(String id, Class c) {
        return mongoTemplate.findById(id, c);
    }

    @Override
    public Object findByIds(String idName, List<String> ids, Class c) {
        Criteria in = Criteria.where(idName).in(ids);
        Query query = new Query(in);
        return mongoTemplate.find(query, c);
    }

    @Override
    public Object findOne(Criteria criteria, Class c, String collectionName) {
        Query query = new Query(criteria);
        return mongoTemplate.findOne(query, c, collectionName);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, List<Sort> sorts) {
        Query query = new Query(criteria);
        if (sorts != null && sorts.size() > 0) {
            for (Sort sort : sorts) {
                query = query.with(sort);
            }
        }
        return mongoTemplate.find(query, c);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, String collectionName) {
        return findByCondition(criteria, c, 0, collectionName);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, int limit, String collectionName) {
        return findByCondition(criteria, c, limit, null, collectionName);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, int limit, Sort sort, String collectionName) {
        Query query = new Query(criteria);
        if (sort != null) {
            query = query.with(sort);
        }
        if (limit > 0) {
            query.limit(limit);
        }
        return mongoTemplate.find(query, c, collectionName);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, Integer pageNum, Integer pageSize, List<Sort> sorts) {
        return findByCondition(criteria, c, null, pageNum, pageSize, sorts);
    }

    @Override
    public Object findByCondition(Criteria criteria, Class c, String collectionName, Integer pageNum, Integer pageSize, List<Sort> sorts) {
        Query query = new Query(criteria);
        if (sorts != null && sorts.size() > 0) {
            for (Sort sort : sorts) {
                query = query.with(sort);
            }
        }
        String actualCollectionName = StringUtils.hasText(collectionName) ? collectionName : getCollectionName(c);
        long count = mongoTemplate.count(query, actualCollectionName);
        mongoPage.initPage(pageNum, pageSize, query);
        List dataList = mongoTemplate.find(query, c, actualCollectionName);
        return mongoPage.pageHelper(count, dataList);
    }

    /**
     * 根据实体类获取对应的 MongoDB 集合名称（collection name）。
     *
     * 该方法首先尝试从实体类上的 {@link Document} 注解中提取集合名称；
     * 如果注解不存在或未指定集合名，则默认使用类名的小写形式作为集合名。
     *
     * @param entityClass 实体类的 Class 对象，必须是映射到 MongoDB 文档的 POJO 类
     * @return 返回对应的 MongoDB 集合名称
     */
    private String getCollectionName(Class<?> entityClass) {
        // 获取实体类上的 @Document 注解
        Document document = (Document) entityClass.getAnnotation(Document.class);

        // 如果注解存在且指定了集合名称，则返回该名称
        if (document != null && StringUtils.hasText(document.collection())) {
            return document.collection();
        }

        // 否则，默认将类名转换为小写作为集合名称
        return entityClass.getSimpleName().toLowerCase();
    }

    @Override
    public long count(Criteria criteria, String collectionName) {
        Query query = new Query(criteria);
        return mongoTemplate.count(query, collectionName);
    }

    @Override
    public Object updateOne(Criteria criteria, Update update, String collectionName) {
        Query query = new Query(criteria);
        return mongoTemplate.updateFirst(query, update, collectionName);
    }

    @Override
    public Object updateMany(Criteria criteria, Update update, String collectionName) {
        Query query = new Query(criteria);
        return mongoTemplate.updateMulti(query, update, collectionName);
    }

    @Override
    public Object delete(Criteria criteria, String collectionName) {
        Query query = new Query(criteria);
        return mongoTemplate.remove(query, collectionName);
    }

    @Override
    public long count(Criteria criteria, Class c) {
        Query query = new Query(criteria);
        return mongoTemplate.count(query, c);
    }

    @Override
    public Object updateOne(Criteria criteria, Update update, Class c) {
        Query query = new Query(criteria);
        return mongoTemplate.updateFirst(query, update, c);
    }

    @Override
    public Object updateMany(Criteria criteria, Update update, Class c) {
        Query query = new Query(criteria);
        return mongoTemplate.updateMulti(query, update, c);
    }

    @Override
    public Object delete(Criteria criteria, Class c) {
        Query query = new Query(criteria);
        return mongoTemplate.remove(query, c);
    }

    @Override
    public Object group(Criteria criteria, Class c, String[] fields, String collectionName, long currentPage, long pageSize) {
        pageSize = pageSize == 0 ? 20 : pageSize;
        currentPage = currentPage == 0 ? 1 : currentPage;

        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria),
                Aggregation.group(fields).count().as("sum"),
                Aggregation.sort(Sort.by(Sort.Direction.DESC, "sum")),
                Aggregation.skip((currentPage - 1) * pageSize),
                Aggregation.limit(pageSize));

        AggregationResults aggregate = mongoTemplate.aggregate(aggregation, collectionName, c);
        return mongoPage.pageHelper(currentPage, pageSize, aggregate.getMappedResults());
    }

    @Override
    public Object group(Criteria criteria, Class c, String[] fields, String collectionName) {
        Aggregation aggregation = Aggregation.newAggregation(Aggregation.match(criteria),
                Aggregation.group(fields).count().as("sum"),
                Aggregation.sort(Sort.by(Sort.Direction.DESC, "sum")));

        AggregationResults aggregate = mongoTemplate.aggregate(aggregation, collectionName, c);
        return aggregate.getMappedResults();
    }

    @Override
    public Object findPropertyByCondition(Criteria criteria, Class c, String collectionName, String[] includes) {
        Query query = new Query(criteria);
        query.fields().exclude("_id");
        if (!StringUtils.hasText(Arrays.toString(includes))) {
            for (String result : includes) {
                query.fields().include(result);
            }
        }
        return mongoTemplate.find(query, c, collectionName);
    }

}

